<template>
  <div>
    <!-- 针对内部含有多个原始组件的二次封装 -->

    <!-- 遍历引用的组件的插槽名 -->
    <!-- 当 v-for 遍历一个对象的时候，键值对是倒过来的，跟数组不一样 -->
    <!-- 这里，_ 是 对象值，name 是 对象名。下面是遍历插槽对象名和值。 -->
    <!-- <template v-for="(_, name) in props.inpSlots" :key="name">
      {{ name }}-----{{ _ }}
    </template> -->

    <!-- 注意，这里 el-input 和 el-select 还用 ...$attrs 的原因是如果二者条件相同触发的时候执行相同的函数，那么直接在父组件中写入圆组件的方法就行，不用再在 inpProps 和 selProps 中重新定义 -->
    <el-input
      ref="elinput"
      v-bind="{ ...$attrs, ...props.inpProps }"
      v-on="{ ...props.inpProps.fun }"
      v-model="iptValue"
    >
      <!-- 插槽/作用域插槽转发 -->
      <template
        v-for="(_, name) in props.inpSlots"
        :key="name"
        #[name]="slotData"
      >
        <slot :name="props.inpSlots[name]" v-bind="slotData || {}"> </slot>
      </template>
    </el-input>
    <br />
    <el-select
      ref="elselect"
      v-bind="{ ...$attrs, ...props.selProps }"
      v-on="{ ...props.selProps.fun }"
      v-model="selValue"
    >
      <!-- 插槽/作用域插槽转发 -->
      <template
        v-for="(_, name) in props.selSlots"
        :key="name"
        #[name]="slotData"
      >
        <slot :name="props.selSlots[name]" v-bind="slotData || {}"> </slot>
      </template>
    </el-select>
    <p>组件内测试双向绑定：</p>
    input====> {{ iptValue }}
    <br />
    select====> {{ selValue }}
  </div>
</template>
<script setup>
// 防止变量透传
defineOptions({
  inheritAttrs: false,
});
import { computed, nextTick, reactive, ref, onMounted } from "vue";
const props = defineProps({
  // input 的属性和方法
  inpProps: {
    type: Object,
    default: () => {
      return {
        // 注意，方法放到这里面
        fun: {},
      };
    },
  },
  // select 的属性和方法
  selProps: {
    type: Object,
    default: () => {
      return {
        // 注意，方法放到这里面
        fun: {},
      };
    },
  },
  // input 的插槽
  inpSlots: {
    type: Object,
    default: () => {},
  },
  // select 的插槽
  selSlots: {
    type: Object,
    default: () => {},
  },
  // input 的 v-model
  vModelInput: {
    type: String,
    default: () => "",
  },
  // select 的 v-model
  vModelSelect: [String, Array],
});

// 获取待封装的组件的元素的 ref，目的是暴露到外面
const elinput = ref();
const elselect = ref();
// 对外暴露的 ref 存储在这个对象中
const elementRefObj = reactive({
  elInputRef: {},
  elSelectRef: {},
});

// 子传父  v-model
const emit = defineEmits(["update:vModelInput", "update:vModelSelect"]);
// 通过 computed 继续进行 v-model 双向绑定。
let iptValue = computed({
  set(value) {
    emit("update:vModelInput", value);
  },
  get: () => {
    return props.vModelInput;
  },
});
// vue3.4 及以上的新写法，可以省略掉计算属性双向绑定，props 以及 defineEmits 中的三方面的东西
// const iptValue = defineModel('vModelInput')

let selValue = computed({
  set(value) {
    emit("update:vModelSelect", value);
  },
  get: () => {
    return props.vModelSelect;
  },
});

// import { useAttrs } from "vue";
onMounted(() => {
  /*   // 查询 $attrs
  const attrs = useAttrs();
  console.log("attrs: ", attrs);
 */
  nextTick(() => {
    elementRefObj.elInputRef = elinput.value;
    elementRefObj.elSelectRef = elselect.value;
  });
});

// 对外暴露这两个被封装的元素及其内部方法（向外传递完整的组件内部的 ref）
defineExpose({
  elementRefObj,
});
</script>

<style lang="scss" scoped></style>
